/*
 * p2spSrvr.cc
 *
 *  Created on: 2009-10-31
 *      Author: root
 */

#include "p2spStream.h"
#include <random.h>

void
StreamSrvrPlayTimer::expire(Event*)
{
	resched(t_->get_chunk_interval());
	t_->SlideOneChunk();
}


static class p2spStreamSrvrClass : public TclClass {
  public:
      p2spStreamSrvrClass() : TclClass("Application/p2sp/p2spStreamSrvr") {}
      TclObject* create(int, const char*const*) {
        return (new p2spStreamSrvr);
      }
} class_p2sp_stream_srvr;

//Constructor

p2spStreamSrvr::p2spStreamSrvr() : running_(0), seqno_(0), dbgno_(0), play_timer_(this)
{

	  bind("chunksize_", &chunksize_);
	  bind("rd_wnd_", &rd_wnd_);
	  bind("filesize_", &filesize_);
	  bind("bufsize_", &bufsize_);
	  bind("sel_scheme_", &sel_);
	  bind_bw("playrate_", &playrate_);
	  bind_bw("wwan_rate_", &wwan_rate_);


}

int
p2spStreamSrvr::command(int argc, const char*const* argv) {
  Tcl& tcl = Tcl::instance();

  if(argc == 3) {
      if (strcmp(argv[1], "attach-agent") == 0) {
          agent_ = (Agent*) TclObject::lookup(argv[2]);
          if (agent_ == 0) {
            tcl.resultf("no such agent %s", argv[2]);
            return(TCL_ERROR);
           }
      } else if(strcmp(argv[1], "stream_file") == 0) {
    	  stream_name_ = new char[strlen(argv[2]) + 1];
    	  strcpy(stream_name_, argv[2]);
      }

  agent_->attachApp(this);
  return(TCL_OK);
  }

  return (Application::command(argc, argv));

}

void
p2spStreamSrvr::start() {

		chunk_interval_ = chunksize_*8/playrate_;
		switch(sel_) {
		  case 1:
			  sel_scheme_ = GREEDY;
			  break;
		  case 2:
			  sel_scheme_ = RAREST_FIRST;
			  break;
		  case 3:
			  sel_scheme_ = MOST_WANTED_GR;
			  break;
		  case 4:
			  sel_scheme_ = MOST_WANTED_RF;
			  break;
		  case 5:
			  sel_scheme_ = MOST_WANTED_RD;
			  break;
		  case 6:
			  sel_scheme_ = RD;
			  break;
		  case 7:
			  sel_scheme_ = MIXED;
			  break;
		  case 8:
			  sel_scheme_ = GREEDY_RD;
			  break;
		  case 9:
			  sel_scheme_ = RAREST_FIRST_RD;
			  break;
		}
		assert(bufsize_ <= CHKBUFSIZE);

		printf(""TIME_FORMAT"\t Node %d Start p2sp App Server\n", CURRENT_TIME, agent_->addr());
		running_ = 1;
		initbuf();
		play_timer_.resched(chunk_interval_);

}



void
p2spStreamSrvr::stop() {
  printf(""TIME_FORMAT"\t Node %d Stop p2sp App Server\n", CURRENT_TIME, agent_->addr());
  running_ = 0;
}


void
p2spStreamSrvr::initbuf() {
	uint16_t chunkid = 0;
	head = new chunkbuf_t[bufsize_];
	if(head == NULL){
		printf("chunk buffer memory allocation failed!\n");
		return;
	}
	chunkbuf_t *p = head;
	p->chunkid = chunkid++;
	p->hasIt = 1;
	p->next = p+1;
	p->prev = head + bufsize_ - 1;
	p++;
	while(p != head + bufsize_ -1) {
		p->chunkid =  chunkid++;
		p->hasIt = 1;
		p->next = p+1;
		p->prev = p-1;
		p++;
	}
	p->chunkid =  chunkid++;
	p->hasIt = 1;
	tail = p;
	tail->next = head;
	tail->prev = p-1;

}

void
p2spStreamSrvr::SlideOneChunk() {
	chunkbuf_t *p;
	p = head;
	head = head->next;
	p->chunkid = tail->chunkid + 1;
	p->hasIt = 1;
	tail = p;

}

void
p2spStreamSrvr::recv_msg(int nbytes, const char *msg) {

}


/*
 * A stream server will receive request from the client.
 * The request will include the frame number which the client is currently
 * playing and a bitmap of the frames that it has and it wants. For example,
 * if the buffer time is about 5sec (150frames), and the client is current play frame No. 7
 * and the client has the frames from No.8 to No. 15, and No. 17 19, then the
 * request sent by this client will include a bitmap indicating the requested frames are
 * frame No. 16 No. 18 No.20 to No. 156. The server will then choose frames to send.
 */

void
p2spStreamSrvr::process_data(int size, AppData* data) {


    if(running_)
      if(data){
            //message process in p2sp Server
            //p2spAppData* p2sp_data = (p2spAppData*) data;
        PacketData* p2sp_pkt_data = (PacketData*) data;

        p2sp_data_t* p2sp_data = (p2sp_data_t*)(p2sp_pkt_data->data());

        assert(p2sp_data->type_ == SRVR_REQUEST || p2sp_data->type_ == IS_RECVING || p2sp_data->type_ == CHUNKMAP_REQ);
		if (p2sp_data->type_ == CHUNKMAP_REQ) {
			send_chunk_map(p2sp_data->src_id_);
		}
		else if (p2sp_data->type_ == SRVR_REQUEST){
			int n = p2sp_data->nbytes_ - sizeof(p2sp_data_t);
			unsigned char* bmp = new unsigned char[n];
			memcpy(bmp, p2sp_pkt_data->data()+sizeof(p2sp_data_t), n);
			int tx_chunkid = select_chunk(p2sp_data->chunk_id_, bmp);
			delete bmp;
//			printf("chunk select strategy: select %d from %d to %d \n", tx_chunkid, p2sp_data->chunk_id_, p2sp_data->chunk_id_ + bufsize_ -1);
			if(tx_chunkid >= 0) send_p2sp_pkt(p2sp_data->src_id_, tx_chunkid);
		}
		delete data;
      }
}

void
p2spStreamSrvr::send_chunk_map(nsaddr_t dst){
	if(running_){
			PacketData* p2sp_pkt_data = new PacketData(sizeof(p2sp_data_t));
		    p2sp_data_t* p2sp_data = (p2sp_data_t*)(p2sp_pkt_data->data());

		    p2sp_data->type_ = CHUNKMAP_CONF;
		    p2sp_data->chunk_id_ = head->chunkid; //send the chunk id of head of the buf to the request
		    p2sp_data->nbytes_ = sizeof(p2sp_data_t); // size of p2sp application data packet, size is equal to chunk size
		    p2sp_data->src_id_ = agent_->addr();
		    p2sp_data->dst_id_ = dst;
		    p2sp_data->seqno_ = ++seqno_;
		    agent_->sendto(p2sp_data->nbytes_, p2sp_pkt_data, 0, dst);
//		    printf(""TIME_FORMAT"\tSRVR: Serv send chunk map to %d\n", CURRENT_TIME, dst);
	}
}

void
p2spStreamSrvr::send_p2sp_pkt(nsaddr_t dst, int chunkid){


  if (running_){
		//data can be sent only if server is in running state
		//p2spAppData* p2sp_data = new p2spAppData();

		PacketData* p2sp_pkt_data = new PacketData(sizeof(p2sp_data_t) + chunksize_);
		p2sp_data_t* p2sp_data = (p2sp_data_t*)(p2sp_pkt_data->data());

		p2sp_data->type_ = SRVR_DATA;
		p2sp_data->chunk_id_ = chunkid; //change 1 to one choose chunk from request bitmap
		p2sp_data->nbytes_ = sizeof(p2sp_data_t) + chunksize_; // size of p2sp application data packet, size is equal to chunk size
		p2sp_data->src_id_ = agent_->addr();
		p2sp_data->dst_id_ = dst;
		p2sp_data->seqno_ = ++seqno_;

		agent_->sendto(p2sp_data->nbytes_, p2sp_pkt_data, 0, dst);


  }

}


int
p2spStreamSrvr::select_chunk(uint16_t chunkid, unsigned char *req_bmp) {
	//return 0 based index of the chunks
	int cid = -1;
	uint16_t min_id, max_id, id, rd_wnd;
	min_id = chunkid > head->chunkid ? chunkid:(head->chunkid);
	max_id = (chunkid+bufsize_-1) > tail->chunkid ? (tail->chunkid):(chunkid+bufsize_-1);
	min_id = min_id + 1;
	max_id = max_id - 1;
	rd_wnd = Random::integer(rd_wnd_);

	if(sel_scheme_ == RAREST_FIRST) {
		for(id = max_id; id >= min_id; id --){
			if ((req_bmp[(id - chunkid) / 8] >> ((id - chunkid)% 8)) & 1 == 1)
				return id;
		}
	}else if (sel_scheme_ == GREEDY) {
		for(id = min_id + playrate_/wwan_rate_; id <= max_id; id ++) {
			if ((req_bmp[(id - chunkid) / 8] >> ((id - chunkid)% 8)) & 1 == 1)
				return id;
		}

	}else if(sel_scheme_ == RAREST_FIRST_RD) {
		for(id = max_id; id >= min_id; id --){
			if ((req_bmp[(id - chunkid) / 8] >> ((id - chunkid)% 8)) & 1 == 1  && rd_wnd >= 0) {
				if(rd_wnd == 0) return id;
				rd_wnd --;
				cid = id;
			}
		}
		if ((req_bmp[(id + 1 - chunkid) / 8] >> ((id + 1 - chunkid)% 8)) & 1 == 1) return (id+1);
		else return cid;

	}else if (sel_scheme_ == GREEDY_RD) {

		for(id = min_id + playrate_/wwan_rate_; id <= max_id; id ++) {
			if ((req_bmp[(id - chunkid) / 8] >> ((id - chunkid)% 8)) & 1 == 1  && rd_wnd >= 0) {
				if(rd_wnd == 0) return id;
				rd_wnd --;
				cid = id;
			}

		}
		if ((req_bmp[(id - 1 - chunkid) / 8] >> ((id - 1 - chunkid)% 8)) & 1 == 1) return (id-1);
		else return cid;

	}else if(sel_scheme_ == MIXED) {
		for(id = max_id; id >= (min_id +max_id)/2; id --) {
				if ((req_bmp[(id - chunkid) / 8] >> ((id - chunkid)% 8)) & 1 == 1)
					return id;
		}
		for(id = min_id; id < (min_id +max_id)/2; id ++) {
				if ((req_bmp[(id - chunkid) / 8] >> ((id - chunkid)% 8)) & 1 == 1)
					return id;
		}
	}
	return -1;

}
